﻿namespace BM.Tools.WebControls.MForm.Design
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.Web.Services.Description;
    using System.Web.UI;
    using System.Windows.Forms;
    using System.Xml;
    using System.Xml.Schema;

    using BM.Tools.WebControls.MForm.Generator;
    using BM.Tools.WebControls.MForm.Generator.Templates;
    using BM.Tools.Xml.Schema;
    using BM.Tools.Xml.Schema.Utils;

    /// <summary>
    /// A form that is used to set the properties of the web form from xml schema generation
    /// </summary>
    public partial class GeneratorForm : Form
    {
        private XmlSchemaSet set;

        private FormItem rootItem;

        private FormBuilderSettings options = new FormBuilderSettings();

        /// <summary>
        /// Initializes a new instance of the <see cref="GeneratorForm"/> class. 
        /// </summary>
        public GeneratorForm()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Gets the generation Options.
        /// </summary>
        public FormBuilderSettings Options
        {
            get
            {
                return options;
            }
        }

        /// <summary>
        /// performs the generation and returns a template and manager pair
        /// </summary>
        /// <returns>
        /// a pair of template and manager objects
        /// </returns>
        [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Justification = "The operation may be expensive")]
        public Pair GetGeneratedData()
        {
            return new Pair(new ItemTemplate(rootItem), rootItem.Builder.Manager);
        }

        private static XmlSchemaSet GetXsdSchema(XmlReader reader)
        {
            XmlSchema schema = XmlSchema.Read(reader, null);
            var schemaSet = new XmlSchemaSet();
            schemaSet.Add(schema);
            return schemaSet;
        }

        private static XmlSchemaSet GetWsdlSchema(XmlReader xr)
        {
            var description = ServiceDescription.Read(xr);
            var schemaSet = new XmlSchemaSet();            
            foreach (XmlSchema schema in description.Types.Schemas)
            {
                schemaSet.Add(schema);
            }

            return schemaSet;
        }

        private FormItem GetFormItem(string localName)
        {
            var schemaSetHelper = new SchemaSetHelper(set);
            var formBuilder = new FormBuilder(set, FormBuilderMode.Edit);
            formBuilder.Settings = Options;
            var elementQName = new XmlQualifiedName(
                localName, schemaSetHelper.MainSchema.TargetNamespace);
            var element = (XmlSchemaElement)set.GlobalElements[elementQName];
            return formBuilder.TraverseElement(element) as FormItem;
        }
        private void FillRootNames(XmlSchemaSet xmlSchemaSet)
        {
            set = xmlSchemaSet;
            var prober = new SchemaProber(set);
            ElementsList.Items.Clear();
            foreach (string rootName in prober.GetRootNames())
            {
                ElementsList.Items.Add(rootName);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Any exception should be caught in this place")]
        private XmlSchemaSet GetSchemaSet(string fileName)
        {
            try
            {
                var schemaSet = PrepareSchemaSet(fileName);
                schemaSet.CompilationSettings.EnableUpaCheck = false;
                schemaSet.Compile();
                return schemaSet;
            }
            catch (Exception e)
            {
                ErrorTextBox.Text = e.Message;
                return null;
            }
        }

        private XmlSchemaSet PrepareSchemaSet(string fileName)
        {
            if (fileName.EndsWith("dll", StringComparison.Ordinal))
            {
                return TypeToSchemaConverter.ExportAssembly(fileName);
            }

            using (XmlReader xr = XmlReader.Create(fileName))
            {
                if (ServiceDescription.CanRead(xr))
                {
                    return GetWsdlSchema(xr);
                }

                return GetXsdSchema(xr);
            }
        }

        private void ChooseButton_Click(object sender, EventArgs e)
        {
            ErrorTextBox.Text = string.Empty;
            var contractLocation = GetContractLocation();
            var schemaSet = GetSchemaSet(contractLocation);
            if (schemaSet == null)
            {
                return;
            }

            FillRootNames(schemaSet);
        }

        private string GetContractLocation()
        {
            if (DiskRadioButton.Checked)
            {
                if (ContractFileDialog.ShowDialog() == DialogResult.OK)
                {
                    ContractTextBox.Text = ContractFileDialog.FileName;
                }
            }

            return ContractTextBox.Text;
        }

        private void GeneratorForm_Load(object sender, EventArgs e)
        {
            OptionsPropertyGrid.SelectedObject = Options;
        }

        private void ElementsList_SelectedIndexChanged(object sender, EventArgs e)
        {
            rootItem = GetFormItem(ElementsList.SelectedItem.ToString());
            FormItemsTree.LoadFormItemTree(rootItem);
        }
    }
}